<!doctype html>
<html lang="zh-CN">
<head>

    <meta charset="utf-8">
    <meta name="generator" content="Hugo 0.74.1" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>CameraX的使用 | caijinglong的博客</title>
    <meta property="og:title" content="CameraX的使用 - caijinglong的博客">
    <meta property="og:type" content="article">
        
    <meta property="article:published_time" content="2019-05-14T13:44:59&#43;08:00">
        
        
    <meta property="article:modified_time" content="2019-05-14T13:44:59&#43;08:00">
        
    <meta name="Keywords" content="golang,go语言,flutter,caijinglong,java,android,博客,项目管理,python,软件架构,公众号,小程序">
    <meta name="description" content="CameraX的使用">
        
    <meta name="author" content="caijinglong">
    <meta property="og:url" content="http://www.kikt.top/posts/android/camera/camerax-usage/">
    <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon">

    <link rel="stylesheet" href="/css/normalize.css">
    
        <link rel="stylesheet" href="/css/prism.css">
    
    <link rel="stylesheet" href="/css/style.css">
    <script type="text/javascript" src="//cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>

    

   
    <script>
        (function(){
            var bp = document.createElement('script');
            var curProtocol = window.location.protocol.split(':')[0];
            if (curProtocol === 'https') {
                bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
            }
            else {
                bp.src = 'http://push.zhanzhang.baidu.com/push.js';
            }
            var s = document.getElementsByTagName("script")[0];
            s.parentNode.insertBefore(bp, s);
        })();
    </script>

    
    
</head>

<body>
<header id="header" class="clearfix">
    <div class="container">
        <div class="col-group">
            <div class="site-name ">
                
                    <a id="logo" href="http://www.kikt.top">
                        caijinglong的博客
                    </a>
                
                <p class="description">Android/Flutter开发者，对于golang/python/ios/java 均有所了解</p>
            </div>
            <div>
                <nav id="nav-menu" class="clearfix">
                    <a class="" href="http://www.kikt.top">首页</a>
                    
                    <a  href="http://www.kikt.top/archives/" title="归档">归档</a>
                    
                    <a  href="http://www.kikt.top/contact/" title="接单">接单</a>
                    
                    <a  href="http://www.kikt.top/about/" title="关于">关于</a>
                    
                </nav>
            </div>
        </div>
    </div>
</header>


<div id="body">
    <div class="container">
        <div class="col-group">

            <div class="col-8" id="main">
                <div class="res-cons">
                    <article class="post">
                        <header>
                            <h1 class="post-title">CameraX的使用</h1>
                        </header>
                        <date class="post-meta meta-date">
                            2019年5月14日
                        </date>
                        
                        <div class="post-meta meta-category">
                            |
                            
                                <a href="http://www.kikt.top/categories/android">android</a>
                            
                        </div>
                        
                        
                        <div class="post-meta">
                            <span id="busuanzi_container_page_pv">|<span id="busuanzi_value_page_pv"></span><span> 阅读</span></span>
                        </div>
                        
                        <div class="post-content">
                            <p>我在 googleIO 前查看演讲主题,看到了有一篇标题是关于 cameraX ,当时在 android 官网没有搜索到教程</p>
<p>在 flutter web 基本体验完毕后, 再去搜索果然从官网查看到了 cameraX 的相关介绍</p>
<p><a href="https://developer.android.com/training/camerax">官网链接</a>
<a href="https://codelabs.developers.google.com/codelabs/camerax-getting-started/#0">官方使用说明</a></p>
<h2 id="目录">目录</h2>
<ul>
<li><a href="#%E7%9B%AE%E5%BD%95">目录</a></li>
<li><a href="#%E4%BB%8B%E7%BB%8D">介绍</a></li>
<li><a href="#%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83">开发环境</a></li>
<li><a href="#%E7%BC%96%E7%A0%81%E5%87%86%E5%A4%87">编码准备</a>
<ul>
<li><a href="#%E6%96%B0%E5%BB%BA%E9%A1%B9%E7%9B%AE">新建项目</a></li>
<li><a href="#%E6%B7%BB%E5%8A%A0%E4%BE%9D%E8%B5%96">添加依赖</a></li>
<li><a href="#%E5%B0%9D%E8%AF%95%E8%BF%90%E8%A1%8C%E9%A1%B9%E7%9B%AE">尝试运行项目</a></li>
</ul>
</li>
<li><a href="#%E7%BC%96%E7%A0%81">编码</a>
<ul>
<li><a href="#%E4%BF%AE%E6%94%B9-xml-%E6%96%87%E4%BB%B6">修改 xml 文件</a></li>
<li><a href="#%E7%94%B3%E8%AF%B7%E6%9D%83%E9%99%90">申请权限</a></li>
<li><a href="#%E5%AE%9E%E7%8E%B0%E7%85%A7%E7%9B%B8%E7%9A%84%E5%8A%9F%E8%83%BD">实现照相的功能</a></li>
<li><a href="#%E6%8B%8D%E7%85%A7">拍照</a></li>
<li><a href="#%E5%88%86%E6%9E%90%E5%99%A8">分析器</a></li>
<li><a href="#%E7%BC%96%E7%A0%81%E7%BB%93%E6%9D%9F">编码结束</a></li>
</ul>
</li>
<li><a href="#%E8%BF%9B%E9%98%B6%E6%8E%A2%E7%B4%A2">进阶探索</a></li>
<li><a href="#%E5%90%8E%E8%AE%B0">后记</a></li>
</ul>
<h2 id="介绍">介绍</h2>
<p>CameraX 是 Jetpack 的一部分, 旨在帮助更好更简单的使用照相机</p>
<p>最低支持的 API 等级是 API 21(5.0)</p>
<h2 id="开发环境">开发环境</h2>
<p>我当前的开发环境是</p>
<p>MacOS 10.13.6<br>
Android Studio 3.4
小米 8 MIUI 10.3 稳定版 10.3.2.0(android 9.0)</p>
<p>最低支持
官方说明为 AndroidStudio 3.3
API 21+的设备</p>
<h2 id="编码准备">编码准备</h2>
<h3 id="新建项目">新建项目</h3>
<p>修改这两项</p>
<p><img src="https://dev.azure.com/cjlspy/844861b8-ee45-48a3-b430-b974684baaaa/_apis/git/repositories/bf4da1c1-1cfa-4f54-8a75-745cce2b737c/items?versionDescriptor%5Bversion%5D=master&amp;path=%2F20190514135542.png&amp;versionDescriptor%5BversionOptions%5D=0&amp;versionDescriptor%5BversionType%5D=0&amp;resolveLfs=true&amp;%24format=octetStream&amp;api-version=5.0" alt="20190514135542.png"></p>
<p>最低 21, 使用 androidX</p>
<h3 id="添加依赖">添加依赖</h3>
<p>在 app 级别的 build.gradle 中添加如下依赖</p>
<p>这个版本当前还是 alpha 版本,后续可能会升级为正式版, 可以查看<a href="https://mvnrepository.com/artifact/androidx.camera/camera-core">mvn 仓库中的版本号</a></p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-groovy" data-lang="groovy">dependencies <span style="color:#f92672">{</span>
    <span style="color:#66d9ef">def</span> camerax_version <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;1.0.0-alpha01&#34;</span>
    implementation <span style="color:#e6db74">&#34;androidx.camera:camera-core:${camerax_version}&#34;</span>
    implementation <span style="color:#e6db74">&#34;androidx.camera:camera-camera2:${camerax_version}&#34;</span>
<span style="color:#f92672">}</span>
</code></pre></div><h3 id="尝试运行项目">尝试运行项目</h3>
<p>运行项目,我这里可以成功跑起来,说明依赖添加是成功的</p>
<p><img src="https://dev.azure.com/cjlspy/844861b8-ee45-48a3-b430-b974684baaaa/_apis/git/repositories/bf4da1c1-1cfa-4f54-8a75-745cce2b737c/items?versionDescriptor%5Bversion%5D=master&amp;path=%2Fdevice-2019-05-14-141300.png&amp;versionDescriptor%5BversionOptions%5D=0&amp;versionDescriptor%5BversionType%5D=0&amp;resolveLfs=true&amp;%24format=octetStream&amp;api-version=5.0" alt="device-2019-05-14-141300.png"></p>
<h2 id="编码">编码</h2>
<h3 id="修改-xml-文件">修改 xml 文件</h3>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-xml" data-lang="xml"><span style="color:#75715e">&lt;?xml version=&#34;1.0&#34; encoding=&#34;utf-8&#34;?&gt;</span>
<span style="color:#f92672">&lt;androidx.constraintlayout.widget.ConstraintLayout</span>
        <span style="color:#a6e22e">xmlns:android=</span><span style="color:#e6db74">&#34;http://schemas.android.com/apk/res/android&#34;</span>
        <span style="color:#a6e22e">xmlns:tools=</span><span style="color:#e6db74">&#34;http://schemas.android.com/tools&#34;</span>
        <span style="color:#a6e22e">xmlns:app=</span><span style="color:#e6db74">&#34;http://schemas.android.com/apk/res-auto&#34;</span>
        <span style="color:#a6e22e">android:layout_width=</span><span style="color:#e6db74">&#34;match_parent&#34;</span>
        <span style="color:#a6e22e">android:layout_height=</span><span style="color:#e6db74">&#34;match_parent&#34;</span>
        <span style="color:#a6e22e">tools:context=</span><span style="color:#e6db74">&#34;.MainActivity&#34;</span><span style="color:#f92672">&gt;</span>

    <span style="color:#f92672">&lt;TextureView</span>
            <span style="color:#a6e22e">android:id=</span><span style="color:#e6db74">&#34;@+id/view_finder&#34;</span>
            <span style="color:#a6e22e">android:layout_width=</span><span style="color:#e6db74">&#34;640px&#34;</span>
            <span style="color:#a6e22e">android:layout_height=</span><span style="color:#e6db74">&#34;640px&#34;</span>
            <span style="color:#a6e22e">app:layout_constraintTop_toTopOf=</span><span style="color:#e6db74">&#34;parent&#34;</span>
            <span style="color:#a6e22e">app:layout_constraintBottom_toBottomOf=</span><span style="color:#e6db74">&#34;parent&#34;</span>
            <span style="color:#a6e22e">app:layout_constraintStart_toStartOf=</span><span style="color:#e6db74">&#34;parent&#34;</span>
            <span style="color:#a6e22e">app:layout_constraintEnd_toEndOf=</span><span style="color:#e6db74">&#34;parent&#34;</span> <span style="color:#f92672">/&gt;</span>

<span style="color:#f92672">&lt;/androidx.constraintlayout.widget.ConstraintLayout&gt;</span>
</code></pre></div><p>使用<code>TextureView</code>作为显示的 View</p>
<h3 id="申请权限">申请权限</h3>
<p>添加到 manifest</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-xml" data-lang="xml"><span style="color:#f92672">&lt;uses-permission</span> <span style="color:#a6e22e">android:name=</span><span style="color:#e6db74">&#34;android.permission.CAMERA&#34;</span> <span style="color:#f92672">/&gt;</span>
</code></pre></div><p>申请动态访问权限,这一步可以借助第三方插件,也可以自己写</p>
<p>截至目前为止,代码如下</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin"><span style="color:#66d9ef">package</span> top.kikt.camerax.usage

<span style="color:#66d9ef">import</span> android.Manifest
<span style="color:#66d9ef">import</span> android.content.pm.PackageManager
<span style="color:#66d9ef">import</span> android.os.Bundle
<span style="color:#66d9ef">import</span> android.view.TextureView
<span style="color:#66d9ef">import</span> android.widget.Toast
<span style="color:#66d9ef">import</span> androidx.appcompat.app.AppCompatActivity
<span style="color:#66d9ef">import</span> androidx.core.app.ActivityCompat
<span style="color:#66d9ef">import</span> androidx.core.content.ContextCompat
<span style="color:#66d9ef">import</span> kotlinx.android.synthetic.main.activity_main.*

<span style="color:#66d9ef">private</span> <span style="color:#66d9ef">const</span> <span style="color:#66d9ef">val</span> REQUEST_CODE_PERMISSIONS = <span style="color:#ae81ff">10</span>
<span style="color:#66d9ef">private</span> <span style="color:#66d9ef">val</span> REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA)

<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MainActivity</span> : AppCompatActivity(), LifecycleOwner {

    <span style="color:#66d9ef">private</span> <span style="color:#66d9ef">lateinit</span> <span style="color:#66d9ef">var</span> viewFinder: TextureView

    <span style="color:#66d9ef">override</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">onCreate</span>(savedInstanceState: Bundle?) {
        <span style="color:#66d9ef">super</span>.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        viewFinder = view_finder

        <span style="color:#75715e">// Request camera permissions
</span><span style="color:#75715e"></span>        <span style="color:#66d9ef">if</span> (allPermissionsGranted()) {
            viewFinder.post { startCamera() }
        } <span style="color:#66d9ef">else</span> {
            ActivityCompat.requestPermissions(
                <span style="color:#66d9ef">this</span>, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS
            )
        }

        <span style="color:#75715e">// Every time the provided texture view changes, recompute layout
</span><span style="color:#75715e"></span>        viewFinder.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ -&gt;
            updateTransform()
        }
    }


    <span style="color:#66d9ef">private</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">startCamera</span>() {

    }

    <span style="color:#66d9ef">private</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">updateTransform</span>() {}

    <span style="color:#75715e">/**
</span><span style="color:#75715e">     * Process result from permission request dialog box, has the request
</span><span style="color:#75715e">     * been granted? If yes, start Camera. Otherwise display a toast
</span><span style="color:#75715e">     */</span>
    <span style="color:#66d9ef">override</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">onRequestPermissionsResult</span>(
        requestCode: Int, permissions: Array&lt;String&gt;, grantResults: IntArray
    ) {
        <span style="color:#66d9ef">if</span> (requestCode == REQUEST_CODE_PERMISSIONS) {
            <span style="color:#66d9ef">if</span> (allPermissionsGranted()) {
                viewFinder.post { startCamera() }
            } <span style="color:#66d9ef">else</span> {
                Toast.makeText(
                    <span style="color:#66d9ef">this</span>,
                    <span style="color:#e6db74">&#34;Permissions not granted by the user.&#34;</span>,
                    Toast.LENGTH_SHORT
                ).show()
                finish()
            }
        }
    }

    <span style="color:#75715e">/**
</span><span style="color:#75715e">     * Check if all permission specified in the manifest have been granted
</span><span style="color:#75715e">     */</span>
    <span style="color:#66d9ef">private</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">allPermissionsGranted</span>(): Boolean {
        <span style="color:#66d9ef">for</span> (permission <span style="color:#66d9ef">in</span> REQUIRED_PERMISSIONS) {
            <span style="color:#66d9ef">if</span> (ContextCompat.checkSelfPermission(
                    <span style="color:#66d9ef">this</span>, permission
                ) != PackageManager.PERMISSION_GRANTED
            ) {
                <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">false</span>
            }
        }
        <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">true</span>
    }
}

</code></pre></div><h3 id="实现照相的功能">实现照相的功能</h3>
<p>实现 startCamera 的逻辑</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin">    <span style="color:#66d9ef">private</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">startCamera</span>() {

        <span style="color:#75715e">// Create configuration object for the viewfinder use case
</span><span style="color:#75715e"></span>        <span style="color:#66d9ef">val</span> previewConfig = PreviewConfig.Builder().apply {
            setTargetAspectRatio(Rational(<span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">1</span>))
            setTargetResolution(Size(<span style="color:#ae81ff">640</span>, <span style="color:#ae81ff">640</span>))
        }.build()

        <span style="color:#75715e">// Build the viewfinder use case
</span><span style="color:#75715e"></span>        <span style="color:#66d9ef">val</span> preview = Preview(previewConfig)

        <span style="color:#75715e">// Every time the viewfinder is updated, recompute layout
</span><span style="color:#75715e"></span>        preview.setOnPreviewOutputUpdateListener {

            <span style="color:#75715e">// To update the SurfaceTexture, we have to remove it and re-add it
</span><span style="color:#75715e"></span>            <span style="color:#66d9ef">val</span> parent = viewFinder.parent <span style="color:#66d9ef">as</span> ViewGroup
            parent.removeView(viewFinder)
            parent.addView(viewFinder, <span style="color:#ae81ff">0</span>)

            viewFinder.surfaceTexture = it.surfaceTexture
            updateTransform()
        }

        <span style="color:#75715e">// Bind use cases to lifecycle
</span><span style="color:#75715e"></span>        <span style="color:#75715e">// If Android Studio complains about &#34;this&#34; being not a LifecycleOwner
</span><span style="color:#75715e"></span>        <span style="color:#75715e">// try rebuilding the project or updating the appcompat dependency to
</span><span style="color:#75715e"></span>        <span style="color:#75715e">// version 1.1.0 or higher.
</span><span style="color:#75715e"></span>        CameraX.bindToLifecycle(<span style="color:#66d9ef">this</span>, preview)
    }

    <span style="color:#66d9ef">private</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">updateTransform</span>() {
        <span style="color:#66d9ef">val</span> matrix = Matrix()

        <span style="color:#75715e">// 计算中心
</span><span style="color:#75715e"></span>        <span style="color:#66d9ef">val</span> centerX = viewFinder.width / <span style="color:#ae81ff">2f</span>
        <span style="color:#66d9ef">val</span> centerY = viewFinder.height / <span style="color:#ae81ff">2f</span>

        <span style="color:#75715e">// 纠正屏幕方向的错误
</span><span style="color:#75715e"></span>        <span style="color:#66d9ef">val</span> rotationDegrees = <span style="color:#66d9ef">when</span> (viewFinder.display.rotation) {
            Surface.ROTATION_0 -&gt; <span style="color:#ae81ff">0</span>
            Surface.ROTATION_90 -&gt; <span style="color:#ae81ff">90</span>
            Surface.ROTATION_180 -&gt; <span style="color:#ae81ff">180</span>
            Surface.ROTATION_270 -&gt; <span style="color:#ae81ff">270</span>
            <span style="color:#66d9ef">else</span> -&gt; <span style="color:#66d9ef">return</span>
        }
        matrix.postRotate(-rotationDegrees.toFloat(), centerX, centerY)

        <span style="color:#75715e">// 把纠正错误后的矩阵传给viewFinder
</span><span style="color:#75715e"></span>        viewFinder.setTransform(matrix)
    }

</code></pre></div><p>可能遇到的错误:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">java.lang.NoSuchMethodError: No super method getLifecycle<span style="color:#f92672">()</span>Landroidx/lifecycle/Lifecycle; in class Landroidx/core/app/ComponentActivity; or its super classes <span style="color:#f92672">(</span>declaration of <span style="color:#e6db74">&#39;androidx.core.app.ComponentActivity&#39;</span> appears in ........
</code></pre></div><p>在<a href="https://stackoverflow.com/questions/53229259/no-super-method-getlifecycle-after-migrating-to-androidx">Stack Overflow</a>上找到了一个解释</p>
<p>简单来说, 修改一下依赖</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-groovy" data-lang="groovy"><span style="color:#75715e">//    implementation &#39;androidx.appcompat:appcompat:1.0.2&#39;
</span><span style="color:#75715e"></span>    implementation <span style="color:#e6db74">&#39;androidx.appcompat:appcompat:1.1.0-alpha05&#39;</span>
</code></pre></div><p>因为 1.0.2 版本中还没有实现 LifecycleOwner 的接口, 然后就可以删掉 Activity 声明上的 LifecycleOwner 了</p>
<p>1.1.0-alpha05 版本的继承关系图如下:</p>
<p><!-- raw HTML omitted --></p>
<p>然后目前的预览是这样的:</p>
<p><img src="https://dev.azure.com/cjlspy/844861b8-ee45-48a3-b430-b974684baaaa/_apis/git/repositories/bf4da1c1-1cfa-4f54-8a75-745cce2b737c/items?versionDescriptor%5Bversion%5D=master&amp;path=%2F31698213-5C2F-4B1C-BD70-2C51628ABF6A.png&amp;versionDescriptor%5BversionOptions%5D=0&amp;versionDescriptor%5BversionType%5D=0&amp;resolveLfs=true&amp;%24format=octetStream&amp;api-version=5.0" alt="31698213-5C2F-4B1C-BD70-2C51628ABF6A.png"></p>
<p>预览这一步就完成了,相对于以前的 api 来说, 真实的编码量很小</p>
<h3 id="拍照">拍照</h3>
<p>接着就是获取当前的画面了</p>
<p>先添加一个按钮</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-xml" data-lang="xml"><span style="color:#f92672">&lt;ImageButton</span>
        <span style="color:#a6e22e">android:id=</span><span style="color:#e6db74">&#34;@+id/capture_button&#34;</span>
        <span style="color:#a6e22e">android:layout_width=</span><span style="color:#e6db74">&#34;72dp&#34;</span>
        <span style="color:#a6e22e">android:layout_height=</span><span style="color:#e6db74">&#34;72dp&#34;</span>
        <span style="color:#a6e22e">android:layout_margin=</span><span style="color:#e6db74">&#34;24dp&#34;</span>
        <span style="color:#a6e22e">app:srcCompat=</span><span style="color:#e6db74">&#34;@android:drawable/ic_menu_camera&#34;</span>
        <span style="color:#a6e22e">app:layout_constraintBottom_toBottomOf=</span><span style="color:#e6db74">&#34;parent&#34;</span>
        <span style="color:#a6e22e">app:layout_constraintEnd_toEndOf=</span><span style="color:#e6db74">&#34;parent&#34;</span>
        <span style="color:#a6e22e">app:layout_constraintStart_toStartOf=</span><span style="color:#e6db74">&#34;parent&#34;</span> <span style="color:#f92672">/&gt;</span>
</code></pre></div><p>修改 startCamera 方法</p>
<p>添加一段代码</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin">     <span style="color:#75715e">// 添加拍照的代码, 这里和预览的相同
</span><span style="color:#75715e"></span>        <span style="color:#66d9ef">val</span> imageCaptureConfig = ImageCaptureConfig.Builder()
            .apply {
                setTargetAspectRatio(Rational(<span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">1</span>))
                <span style="color:#75715e">// setTargetResolution(Size(640, 640)) // 设置这个,但实际获取则会根据这个有所不同,根据注释说明, 会是与目标更接近的一个分辨率, 但是可能会因为设备的不同而造成可能崩溃的问题, 不确定就不设置
</span><span style="color:#75715e"></span>                setCaptureMode(ImageCapture.CaptureMode.MIN_LATENCY)
            }.build()

        <span style="color:#66d9ef">val</span> imageCapture = ImageCapture(imageCaptureConfig)
        <span style="color:#75715e">// 设置一个点击事件
</span><span style="color:#75715e"></span>        capture_button.setOnClickListener {
            <span style="color:#66d9ef">val</span> file = File(
                externalMediaDirs.first(),
                <span style="color:#e6db74">&#34;${System.currentTimeMillis()}.jpg&#34;</span>
            )

            <span style="color:#75715e">// 捕捉图片
</span><span style="color:#75715e"></span>            imageCapture.takePicture(file, <span style="color:#66d9ef">object</span> <span style="color:#960050;background-color:#1e0010">: </span><span style="color:#a6e22e">ImageCapture</span>.OnImageSavedListener {
                <span style="color:#66d9ef">override</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">onError</span>(
                    error: ImageCapture.UseCaseError,
                    message: String, exc: Throwable?
                ) {
                    <span style="color:#66d9ef">val</span> msg = <span style="color:#e6db74">&#34;Photo capture failed: $message&#34;</span>
                    Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
                    Log.e(<span style="color:#e6db74">&#34;CameraXApp&#34;</span>, msg)
                    exc<span style="color:#f92672">?.</span>printStackTrace()
                }

                <span style="color:#66d9ef">override</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">onImageSaved</span>(file: File) {
                    <span style="color:#66d9ef">val</span> msg = <span style="color:#e6db74">&#34;Photo capture succeeded: ${file.absolutePath}&#34;</span>
                    Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
                    Log.d(<span style="color:#e6db74">&#34;CameraXApp&#34;</span>, msg)
                }
            })
        }
</code></pre></div><p>别忘了修改这里</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin">CameraX.bindToLifecycle(<span style="color:#66d9ef">this</span>, preview, imageCapture)
</code></pre></div><p>然后点击拍照按钮</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">2019-05-14 15:20:50.839 13521-13521/top.kikt.camerax.usage D/CameraXApp: Photo capture succeeded: /storage/emulated/0/Android/media/top.kikt.camerax.usage/1557818450473.jpg
</code></pre></div><p>可以看到我们成功的拍了一张照片</p>
<p>使用 adb 命令导出这个图片, 没有配置的话建议你去配置一下</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">adb pull /storage/emulated/0/Android/media/top.kikt.camerax.usage/1557818450473.jpg
open 1557818450473.jpg
</code></pre></div><p><img src="https://dev.azure.com/cjlspy/844861b8-ee45-48a3-b430-b974684baaaa/_apis/git/repositories/bf4da1c1-1cfa-4f54-8a75-745cce2b737c/items?versionDescriptor%5Bversion%5D=master&amp;path=%2F20190514152242.png&amp;versionDescriptor%5BversionOptions%5D=0&amp;versionDescriptor%5BversionType%5D=0&amp;resolveLfs=true&amp;%24format=octetStream&amp;api-version=5.0" alt="20190514152242.png"></p>
<h3 id="分析器">分析器</h3>
<p>新建一个分析器类,需要继承 <code>ImageAnalysis.Analyzer</code></p>
<p>备注: 这个代码来自于官方示例(开篇那个连接)</p>
<p>目的是记录平均亮度</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin">
<span style="color:#66d9ef">import</span> android.util.Log
<span style="color:#66d9ef">import</span> androidx.camera.core.ImageAnalysis
<span style="color:#66d9ef">import</span> androidx.camera.core.ImageProxy
<span style="color:#66d9ef">import</span> java.nio.ByteBuffer
<span style="color:#66d9ef">import</span> java.util.concurrent.TimeUnit

<span style="color:#75715e">/// create 2019-05-14 by cai
</span><span style="color:#75715e"></span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">LuminosityAnalyzer</span> : ImageAnalysis.Analyzer {
    <span style="color:#66d9ef">private</span> <span style="color:#66d9ef">var</span> lastAnalyzedTimestamp = <span style="color:#ae81ff">0L</span>

    <span style="color:#75715e">/**
</span><span style="color:#75715e">     * Helper extension function used to extract a byte array from an
</span><span style="color:#75715e">     * image plane buffer
</span><span style="color:#75715e">     */</span>
    <span style="color:#66d9ef">private</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">ByteBuffer</span>.toByteArray(): ByteArray {
        rewind()    <span style="color:#75715e">// Rewind the buffer to zero
</span><span style="color:#75715e"></span>        <span style="color:#66d9ef">val</span> data = ByteArray(remaining())
        <span style="color:#66d9ef">get</span>(<span style="color:#66d9ef">data</span>)   <span style="color:#75715e">// Copy the buffer into a byte array
</span><span style="color:#75715e"></span>        <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">data</span> <span style="color:#75715e">// Return the byte array
</span><span style="color:#75715e"></span>    }

    <span style="color:#66d9ef">override</span> <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">analyze</span>(image: ImageProxy, rotationDegrees: Int) {
        <span style="color:#66d9ef">val</span> currentTimestamp = System.currentTimeMillis()
        <span style="color:#75715e">// Calculate the average luma no more often than every second
</span><span style="color:#75715e"></span>        <span style="color:#66d9ef">if</span> (currentTimestamp - lastAnalyzedTimestamp &gt;=
            TimeUnit.SECONDS.toMillis(<span style="color:#ae81ff">1</span>)
        ) {
            <span style="color:#75715e">// Since format in ImageAnalysis is YUV, image.planes[0]
</span><span style="color:#75715e"></span>            <span style="color:#75715e">// contains the Y (luminance) plane
</span><span style="color:#75715e"></span>            <span style="color:#66d9ef">val</span> buffer = image.planes[<span style="color:#ae81ff">0</span>].buffer
            <span style="color:#75715e">// Extract image data from callback object
</span><span style="color:#75715e"></span>            <span style="color:#66d9ef">val</span> data = buffer.toByteArray()
            <span style="color:#75715e">// Convert the data into an array of pixel values
</span><span style="color:#75715e"></span>            <span style="color:#66d9ef">val</span> pixels = <span style="color:#66d9ef">data</span>.map { it.toInt() and <span style="color:#ae81ff">0xFF</span> }
            <span style="color:#75715e">// Compute average luminance for the image
</span><span style="color:#75715e"></span>            <span style="color:#66d9ef">val</span> luma = pixels.average()
            <span style="color:#75715e">// Log the new luma value
</span><span style="color:#75715e"></span>            Log.d(<span style="color:#e6db74">&#34;CameraXApp&#34;</span>, <span style="color:#e6db74">&#34;Average luminosity: $luma&#34;</span>)
            <span style="color:#75715e">// Update timestamp of last analyzed frame
</span><span style="color:#75715e"></span>            lastAnalyzedTimestamp = currentTimestamp
        }
    }
}
</code></pre></div><p>还是回到<code>startCamera</code>方法</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin">
    <span style="color:#75715e">/// 统计配置
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">val</span> analyzerConfig = ImageAnalysisConfig.Builder().apply {
        <span style="color:#75715e">// Use a worker thread for image analysis to prevent glitches
</span><span style="color:#75715e"></span>        <span style="color:#66d9ef">val</span> analyzerThread = HandlerThread(
            <span style="color:#e6db74">&#34;LuminosityAnalysis&#34;</span>
        ).apply { start() }
        setCallbackHandler(Handler(analyzerThread.looper))
        <span style="color:#75715e">// In our analysis, we care more about the latest image than
</span><span style="color:#75715e"></span>        <span style="color:#75715e">// analyzing *every* image
</span><span style="color:#75715e"></span>        setImageReaderMode(ImageAnalysis.ImageReaderMode.ACQUIRE_LATEST_IMAGE)
    }.build()

    <span style="color:#75715e">// 设置
</span><span style="color:#75715e"></span>    <span style="color:#66d9ef">val</span> analyzerUseCase = ImageAnalysis(analyzerConfig).apply {
        analyzer = LuminosityAnalyzer()
    }

</code></pre></div><p>别忘了绑定生命周期</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-kotlin" data-lang="kotlin">    <span style="color:#75715e">// 绑定生命周期和CameraX , 这里第二步修改的时候别忘了把imageCapture 也一起绑定上
</span><span style="color:#75715e"></span>    <span style="color:#75715e">// 同理,第三步的时候需要绑上分析器
</span><span style="color:#75715e"></span>    CameraX.bindToLifecycle(<span style="color:#66d9ef">this</span>, preview, imageCapture, analyzerUseCase)
</code></pre></div><p>接着重新运行代码,随着预览的图像不同会呈现一个平均亮度</p>
<pre><code class="language-log" data-lang="log">2019-05-14 15:37:32.883 14412-14435/top.kikt.camerax.usage D/CameraXApp: Average luminosity: 136.85490234375
2019-05-14 15:37:33.889 14412-14435/top.kikt.camerax.usage D/CameraXApp: Average luminosity: 118.26471354166667
2019-05-14 15:37:34.883 14412-14435/top.kikt.camerax.usage D/CameraXApp: Average luminosity: 137.15953450520834
2019-05-14 15:37:35.934 14412-14435/top.kikt.camerax.usage D/CameraXApp: Average luminosity: 136.98435221354165
2019-05-14 15:37:36.929 14412-14435/top.kikt.camerax.usage D/CameraXApp: Average luminosity: 137.244296875
2019-05-14 15:37:37.964 14412-14435/top.kikt.camerax.usage D/CameraXApp: Average luminosity: 136.60428059895833
2019-05-14 15:37:38.984 14412-14435/top.kikt.camerax.usage D/CameraXApp: Average luminosity: 136.93064127604165
2019-05-14 15:37:39.967 14412-14435/top.kikt.camerax.usage D/CameraXApp: Average luminosity: 125.22169921875
2019-05-14 15:37:41.001 14412-14435/top.kikt.camerax.usage D/CameraXApp: Average luminosity: 115.343046875
2019-05-14 15:37:42.045 14412-14435/top.kikt.camerax.usage D/CameraXApp: Average luminosity: 107.64242838541666
2019-05-14 15:37:43.061 14412-14435/top.kikt.camerax.usage D/CameraXApp: Average luminosity: 76.732939453125
2019-05-14 15:37:44.082 14412-14435/top.kikt.camerax.usage D/CameraXApp: Average luminosity: 112.82620442708334
2019-05-14 15:37:45.105 14412-14435/top.kikt.camerax.usage D/CameraXApp: Average luminosity: 116.12317057291666
2019-05-14 15:37:46.086 14412-14435/top.kikt.camerax.usage D/CameraXApp: Average luminosity: 115.94841796875
2019-05-14 15:37:47.142 14412-14435/top.kikt.camerax.usage D/CameraXApp: Average luminosity: 117.0526171875
2019-05-14 15:37:48.165 14412-14435/top.kikt.camerax.usage D/CameraXApp: Average luminosity: 114.96781901041666
</code></pre><h3 id="编码结束">编码结束</h3>
<p>到这一步为止, 编码的过程就结束了</p>
<h2 id="进阶探索">进阶探索</h2>
<p>通常来说,相机现在会结合很多其他的用途</p>
<ol>
<li>摄像</li>
<li>直播视频上传(视频通话)</li>
<li>图像识别</li>
<li>增强现实</li>
<li>其他&hellip;</li>
</ol>
<p>别看我说的很热闹,但是让我结合这几个方向来写的话,篇幅不允许是一点, 而且这些东西每一行都是能写一整个系列文章的&hellip;(我才不会说是我不会做呢, 哼 😒)</p>
<p>还记得 <code>ImageAnalysis.Analyzer</code> 这个类吗, 这个类的<code>analyze</code>方法会回调一些信息</p>
<p><img src="https://dev.azure.com/cjlspy/844861b8-ee45-48a3-b430-b974684baaaa/_apis/git/repositories/bf4da1c1-1cfa-4f54-8a75-745cce2b737c/items?versionDescriptor%5Bversion%5D=master&amp;path=%2F20190514154722.png&amp;versionDescriptor%5BversionOptions%5D=0&amp;versionDescriptor%5BversionType%5D=0&amp;resolveLfs=true&amp;%24format=octetStream&amp;api-version=5.0" alt="20190514154722.png"></p>
<p>简单来说, 会回调一些 image 的信息和角度</p>
<p>ImageProxy 中包含很多的信息</p>
<p>常用的有:</p>
<p><code>getFormat</code>: 视频格式 ,具体查看<a href="https://developer.android.com/reference/android/media/Image.html#getFormat()">文档</a>
<img src="https://dev.azure.com/cjlspy/844861b8-ee45-48a3-b430-b974684baaaa/_apis/git/repositories/bf4da1c1-1cfa-4f54-8a75-745cce2b737c/items?versionDescriptor%5Bversion%5D=master&amp;path=%2F20190514162133.png&amp;versionDescriptor%5BversionOptions%5D=0&amp;versionDescriptor%5BversionType%5D=0&amp;resolveLfs=true&amp;%24format=octetStream&amp;api-version=5.0" alt="20190514162133.png"></p>
<p>一般来说都应该是<code>YUV_420_888</code></p>
<p>getWidth: 宽度</p>
<p>getHeight: 高度</p>
<p>getTimeStamp: 据说是纳秒单位, 和设备的时间基有关, 我是没看懂什么意思 😁</p>
<p>getPlanes: 视频数据, 类型是<code>PlaneProxy[]</code> kotlin 对应:<code>Array&lt;PlaneProxy&gt;</code>, 这东西的 size 是根据 getFormat 的格式决定的</p>
<p>关于 YUV_420_888 这部分可以查看<a href="https://www.polarxiong.com/archives/Android-Image%E7%B1%BB%E6%B5%85%E6%9E%90-%E7%BB%93%E5%90%88YUV_420_888.html">Android-Image 类浅析-结合 YUV_420_888</a> 和 <a href="https://www.polarxiong.com/archives/Android-YUV_420_888%E7%BC%96%E7%A0%81Image%E8%BD%AC%E6%8D%A2%E4%B8%BAI420%E5%92%8CNV21%E6%A0%BC%E5%BC%8Fbyte%E6%95%B0%E7%BB%84.html">Android: YUV_420_888 编码 Image 转换为 I420 和 NV21 格式 byte 数组</a></p>
<p>然后通过解析数据封装成需要的格式就可以了</p>
<h2 id="后记">后记</h2>
<p>简单来说, CameraX 的 api 比 Camera2 和 Camera 看起来都要好很多</p>
<p>项目地址: <a href="https://github.com/CaiJingLong/android-cameraX-example">https://github.com/CaiJingLong/android-cameraX-example</a></p>
<p>以上</p>

                        </div>

                        

<div class="post-archive">
    <h2>See Also</h2>
    <ul class="listing">
        
        <li><a href="/posts/flutter/android/profile-1/">在Profile下分析android内存占用</a></li>
        
        <li><a href="/posts/flutter/exists/android/">在原 Android 工程的基础上构建一个flutter工程</a></li>
        
        <li><a href="/posts/android/android-include-ffmpeg/">android 引入ffmpeg</a></li>
        
        <li><a href="/posts/android/android-ffmpeg-compile-so/">ndk编译ffmpeg</a></li>
        
        <li><a href="/posts/flutter/android/finish-activity/">Finish FlutterActivity 源码解析</a></li>
        
    </ul>
</div>


                        <div class="post-meta meta-tags">
                            
                            <ul class="clearfix">
                                
                                <li><a href="http://www.kikt.top/tags/android">android</a></li>
                                
                                <li><a href="http://www.kikt.top/tags/camera">camera</a></li>
                                
                                <li><a href="http://www.kikt.top/tags/cameraX">cameraX</a></li>
                                
                            </ul>
                            
                        </div>
                    </article>
                    
    

    
    
    <div class="post bg-white">
      <script src="https://utteranc.es/client.js"
            repo= "caijinglong/kikt-blog-comment"
            issue-term="pathname"
            theme="github-light"
            crossorigin="anonymous"
            async>
      </script>
    </div>
    
                </div>
            </div>
            <div id="secondary">
    <section class="widget">
        <form id="search" action="//www.google.com/search" method="get" accept-charset="utf-8" target="_blank" _lpchecked="1">
      
      <input type="text" name="q" maxlength="20" placeholder="Search">
      <input type="hidden" name="sitesearch" value="http://www.kikt.top">
      <button type="submit" class="submit icon-search"></button>
</form>
    </section>
    
    <section class="widget">
        <h3 class="widget-title">最近文章</h3>
<ul class="widget-list">
    
    <li>
        <a href="http://www.kikt.top/posts/java/crack/crack1/" title="使用javassist,修改jar包方法实现">使用javassist,修改jar包方法实现</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/posts/github/actions/create/" title="Github action 的开发到发布">Github action 的开发到发布</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/posts/flutter/jenkins&#43;fastlane/" title="flutter Jenkins&#43;fastlane 自动化打测试包, 并上传蒲公英">flutter Jenkins&#43;fastlane 自动化打测试包, 并上传蒲公英</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/posts/go/compile-for-android/" title="编译 go 源码为 android 动态库(so)">编译 go 源码为 android 动态库(so)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/posts/flutter/focusnode-2/" title="Flutter FocusNode 焦点那点事-(二)">Flutter FocusNode 焦点那点事-(二)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/posts/flutter/focusnode-1/" title="Flutter FocusNode 焦点那点事-(一)">Flutter FocusNode 焦点那点事-(一)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/posts/server/caddy/cros_for_local_dev/" title="用 Caddy 解决 web 开发中本地跨域的问题">用 Caddy 解决 web 开发中本地跨域的问题</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/posts/flutter/route/navigator-helper2/" title="Navigator Helper2">Navigator Helper2</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/posts/flutter/grey-app/" title="flutter 怎么实现app整体灰度">flutter 怎么实现app整体灰度</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/posts/flutter/plugin/flutter-sdk-import-aar/" title="Flutter 插件开发之引入aar到安卓部分 并使用本地maven">Flutter 插件开发之引入aar到安卓部分 并使用本地maven</a>
    </li>
    
</ul>
    </section>

    

    <section class="widget">
        <h3 class="widget-title">分类</h3>
<ul class="widget-list">
    
    <li>
        <a href="http://www.kikt.top/categories/android/">android(25)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/caddy/">caddy(1)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/dart/">dart(5)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/docker/">docker(3)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/flutter/">flutter(60)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/github/">github(3)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/go/">go(1)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/golang/">golang(1)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/iOS/">iOS(6)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/ios/">ios(1)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/java/">java(6)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/jetbrains/">jetbrains(1)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/other/">other(3)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/python/">python(1)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/server/">server(5)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/spring/">spring(3)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/tools/">tools(1)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/%E6%9D%82%E9%A1%B9/">杂项(1)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/%E6%B6%82%E9%B8%A6/">涂鸦(1)</a>
    </li>
    
</ul>
    </section>

    <section class="widget">
        <h3 class="widget-title">标签</h3>
<div class="tagcloud">
    
    <a href="http://www.kikt.top/tags/10/">10</a>
    
    <a href="http://www.kikt.top/tags/2.3/">2.3</a>
    
    <a href="http://www.kikt.top/tags/Clipboard/">Clipboard</a>
    
    <a href="http://www.kikt.top/tags/Javassist/">Javassist</a>
    
    <a href="http://www.kikt.top/tags/Migrate/">Migrate</a>
    
    <a href="http://www.kikt.top/tags/aar/">aar</a>
    
    <a href="http://www.kikt.top/tags/actions/">actions</a>
    
    <a href="http://www.kikt.top/tags/aidl/">aidl</a>
    
    <a href="http://www.kikt.top/tags/android/">android</a>
    
    <a href="http://www.kikt.top/tags/androidQ/">androidQ</a>
    
    <a href="http://www.kikt.top/tags/androidX/">androidX</a>
    
    <a href="http://www.kikt.top/tags/apk/">apk</a>
    
    <a href="http://www.kikt.top/tags/app/">app</a>
    
    <a href="http://www.kikt.top/tags/appcode/">appcode</a>
    
    <a href="http://www.kikt.top/tags/azure/">azure</a>
    
    <a href="http://www.kikt.top/tags/ble/">ble</a>
    
    <a href="http://www.kikt.top/tags/bottomsheet/">bottomsheet</a>
    
    <a href="http://www.kikt.top/tags/butterknife/">butterknife</a>
    
    <a href="http://www.kikt.top/tags/c/">c</a>
    
    <a href="http://www.kikt.top/tags/caddy/">caddy</a>
    
    <a href="http://www.kikt.top/tags/camera/">camera</a>
    
    <a href="http://www.kikt.top/tags/cameraX/">cameraX</a>
    
    <a href="http://www.kikt.top/tags/channel/">channel</a>
    
    <a href="http://www.kikt.top/tags/cocoapods/">cocoapods</a>
    
    <a href="http://www.kikt.top/tags/cupertino/">cupertino</a>
    
    <a href="http://www.kikt.top/tags/dart/">dart</a>
    
    <a href="http://www.kikt.top/tags/dart-2.6/">dart-2.6</a>
    
    <a href="http://www.kikt.top/tags/desktop/">desktop</a>
    
    <a href="http://www.kikt.top/tags/dialog/">dialog</a>
    
    <a href="http://www.kikt.top/tags/dmg/">dmg</a>
    
    <a href="http://www.kikt.top/tags/doc/">doc</a>
    
    <a href="http://www.kikt.top/tags/docker/">docker</a>
    
    <a href="http://www.kikt.top/tags/docker-compose/">docker-compose</a>
    
    <a href="http://www.kikt.top/tags/excel/">excel</a>
    
    <a href="http://www.kikt.top/tags/exists/">exists</a>
    
    <a href="http://www.kikt.top/tags/faq/">faq</a>
    
    <a href="http://www.kikt.top/tags/fastlane/">fastlane</a>
    
    <a href="http://www.kikt.top/tags/ffi/">ffi</a>
    
    <a href="http://www.kikt.top/tags/ffmpeg/">ffmpeg</a>
    
    <a href="http://www.kikt.top/tags/flexmark/">flexmark</a>
    
    <a href="http://www.kikt.top/tags/flutter/">flutter</a>
    
    <a href="http://www.kikt.top/tags/flutter-web/">flutter-web</a>
    
    <a href="http://www.kikt.top/tags/focus/">focus</a>
    
    <a href="http://www.kikt.top/tags/focusNode/">focusNode</a>
    
    <a href="http://www.kikt.top/tags/form/">form</a>
    
    <a href="http://www.kikt.top/tags/framework/">framework</a>
    
    <a href="http://www.kikt.top/tags/github/">github</a>
    
    <a href="http://www.kikt.top/tags/go/">go</a>
    
    <a href="http://www.kikt.top/tags/golang/">golang</a>
    
    <a href="http://www.kikt.top/tags/gradle/">gradle</a>
    
    <a href="http://www.kikt.top/tags/http/">http</a>
    
    <a href="http://www.kikt.top/tags/iOS/">iOS</a>
    
    <a href="http://www.kikt.top/tags/ide/">ide</a>
    
    <a href="http://www.kikt.top/tags/idea/">idea</a>
    
    <a href="http://www.kikt.top/tags/index/">index</a>
    
    <a href="http://www.kikt.top/tags/inside/">inside</a>
    
    <a href="http://www.kikt.top/tags/ios/">ios</a>
    
    <a href="http://www.kikt.top/tags/java/">java</a>
    
    <a href="http://www.kikt.top/tags/jenkins/">jenkins</a>
    
    <a href="http://www.kikt.top/tags/jetbrains/">jetbrains</a>
    
    <a href="http://www.kikt.top/tags/jsdelivr/">jsdelivr</a>
    
    <a href="http://www.kikt.top/tags/json/">json</a>
    
    <a href="http://www.kikt.top/tags/json_serializable/">json_serializable</a>
    
    <a href="http://www.kikt.top/tags/junit/">junit</a>
    
    <a href="http://www.kikt.top/tags/library/">library</a>
    
    <a href="http://www.kikt.top/tags/live-template/">live-template</a>
    
    <a href="http://www.kikt.top/tags/loadmore/">loadmore</a>
    
    <a href="http://www.kikt.top/tags/log/">log</a>
    
    <a href="http://www.kikt.top/tags/mac/">mac</a>
    
    <a href="http://www.kikt.top/tags/macos/">macos</a>
    
    <a href="http://www.kikt.top/tags/maven/">maven</a>
    
    <a href="http://www.kikt.top/tags/mysql/">mysql</a>
    
    <a href="http://www.kikt.top/tags/navigationbar/">navigationbar</a>
    
    <a href="http://www.kikt.top/tags/navigator/">navigator</a>
    
    <a href="http://www.kikt.top/tags/ndk/">ndk</a>
    
    <a href="http://www.kikt.top/tags/nginx/">nginx</a>
    
    <a href="http://www.kikt.top/tags/oc/">oc</a>
    
    <a href="http://www.kikt.top/tags/opencv/">opencv</a>
    
    <a href="http://www.kikt.top/tags/poi/">poi</a>
    
    <a href="http://www.kikt.top/tags/pub/">pub</a>
    
    <a href="http://www.kikt.top/tags/python/">python</a>
    
    <a href="http://www.kikt.top/tags/retrofit/">retrofit</a>
    
    <a href="http://www.kikt.top/tags/route/">route</a>
    
    <a href="http://www.kikt.top/tags/server/">server</a>
    
    <a href="http://www.kikt.top/tags/so/">so</a>
    
    <a href="http://www.kikt.top/tags/spp/">spp</a>
    
    <a href="http://www.kikt.top/tags/sprintboot/">sprintboot</a>
    
    <a href="http://www.kikt.top/tags/swift/">swift</a>
    
    <a href="http://www.kikt.top/tags/tap/">tap</a>
    
    <a href="http://www.kikt.top/tags/thymeleaf/">thymeleaf</a>
    
    <a href="http://www.kikt.top/tags/tips/">tips</a>
    
    <a href="http://www.kikt.top/tags/toast/">toast</a>
    
    <a href="http://www.kikt.top/tags/transition/">transition</a>
    
    <a href="http://www.kikt.top/tags/ui/">ui</a>
    
    <a href="http://www.kikt.top/tags/utf8/">utf8</a>
    
    <a href="http://www.kikt.top/tags/yield/">yield</a>
    
    <a href="http://www.kikt.top/tags/%E4%BA%8C%E8%BF%9B%E5%88%B6/">二进制</a>
    
    <a href="http://www.kikt.top/tags/%E4%BA%A4%E5%8F%89/">交叉</a>
    
    <a href="http://www.kikt.top/tags/%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%91/">交叉编译</a>
    
    <a href="http://www.kikt.top/tags/%E4%BB%8B%E7%BB%8D/">介绍</a>
    
    <a href="http://www.kikt.top/tags/%E4%BB%A3%E7%A0%81%E8%A7%84%E8%8C%83/">代码规范</a>
    
    <a href="http://www.kikt.top/tags/%E5%86%B2%E7%AA%81/">冲突</a>
    
    <a href="http://www.kikt.top/tags/%E5%8A%A8%E6%80%81/">动态</a>
    
    <a href="http://www.kikt.top/tags/%E5%8F%91%E5%B8%83/">发布</a>
    
    <a href="http://www.kikt.top/tags/%E5%8F%AF%E6%89%A7%E8%A1%8C/">可执行</a>
    
    <a href="http://www.kikt.top/tags/%E5%9B%BE%E5%BA%8A/">图床</a>
    
    <a href="http://www.kikt.top/tags/%E5%9F%8E%E5%B8%82/">城市</a>
    
    <a href="http://www.kikt.top/tags/%E5%A4%A7%E6%96%87%E4%BB%B6/">大文件</a>
    
    <a href="http://www.kikt.top/tags/%E5%B7%A5%E5%85%B7/">工具</a>
    
    <a href="http://www.kikt.top/tags/%E5%B7%B2%E6%9C%89%E9%A1%B9%E7%9B%AE/">已有项目</a>
    
    <a href="http://www.kikt.top/tags/%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83/">开发环境</a>
    
    <a href="http://www.kikt.top/tags/%E6%80%BB%E7%BB%93/">总结</a>
    
    <a href="http://www.kikt.top/tags/%E6%85%A2/">慢</a>
    
    <a href="http://www.kikt.top/tags/%E6%89%93%E5%8C%85/">打包</a>
    
    <a href="http://www.kikt.top/tags/%E6%89%AB%E7%A0%81/">扫码</a>
    
    <a href="http://www.kikt.top/tags/%E6%8F%92%E4%BB%B6/">插件</a>
    
    <a href="http://www.kikt.top/tags/%E6%96%87%E6%A1%A3/">文档</a>
    
    <a href="http://www.kikt.top/tags/%E6%A8%A1%E6%9D%BF/">模板</a>
    
    <a href="http://www.kikt.top/tags/%E6%B8%B2%E6%9F%93/">渲染</a>
    
    <a href="http://www.kikt.top/tags/%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/">源码解析</a>
    
    <a href="http://www.kikt.top/tags/%E7%81%B0%E5%BA%A6/">灰度</a>
    
    <a href="http://www.kikt.top/tags/%E7%82%B9%E5%87%BB/">点击</a>
    
    <a href="http://www.kikt.top/tags/%E7%84%A6%E7%82%B9/">焦点</a>
    
    <a href="http://www.kikt.top/tags/%E7%88%AC%E8%99%AB/">爬虫</a>
    
    <a href="http://www.kikt.top/tags/%E7%8A%B6%E6%80%81%E7%AE%A1%E7%90%86/">状态管理</a>
    
    <a href="http://www.kikt.top/tags/%E7%99%BE%E5%BA%A6%E5%9C%B0%E5%9B%BE/">百度地图</a>
    
    <a href="http://www.kikt.top/tags/%E7%BC%96%E7%A0%81/">编码</a>
    
    <a href="http://www.kikt.top/tags/%E7%BC%96%E7%A8%8B%E6%8A%80%E5%B7%A7/">编程技巧</a>
    
    <a href="http://www.kikt.top/tags/%E7%BC%96%E8%AF%91/">编译</a>
    
    <a href="http://www.kikt.top/tags/%E7%BF%BB%E8%AF%91/">翻译</a>
    
    <a href="http://www.kikt.top/tags/%E8%93%9D%E7%89%99/">蓝牙</a>
    
    <a href="http://www.kikt.top/tags/%E8%A1%A8%E5%8D%95/">表单</a>
    
    <a href="http://www.kikt.top/tags/%E8%B7%AF%E7%94%B1/">路由</a>
    
    <a href="http://www.kikt.top/tags/%E8%BF%87%E5%9C%BA%E5%8A%A8%E7%94%BB/">过场动画</a>
    
    <a href="http://www.kikt.top/tags/%E9%9A%90%E7%A7%81/">隐私</a>
    
</div>
    </section>

    
<section class="widget">
    <h3 class="widget-title">友情链接</h3>
    <ul class="widget-list">
        
        <li>
            <a target="_blank" href="https://github.com/Caijinglong" title="我的Github">我的Github</a>
        </li>
        
        <li>
            <a target="_blank" href="https://flutter.dev" title="flutter 官网">flutter 官网</a>
        </li>
        
    </ul>
</section>


    <section class="widget">
        <h3 class="widget-title">其它</h3>
        <ul class="widget-list">
            <li><a href="http://www.kikt.top/index.xml">文章 RSS</a></li>
        </ul>
    </section>
</div>
        </div>
    </div>
</div>
<footer id="footer">
    <div class="container">
        <div>
            <p>如果我的blog对您有帮助,而您愿意捐赠 😆, 可以用下面的二维码</p>
            <img style="width:5rem;" src="https://dev.azure.com/cjlspy/844861b8-ee45-48a3-b430-b974684baaaa/_apis/git/repositories/bf4da1c1-1cfa-4f54-8a75-745cce2b737c/items?versionDescriptor%5Bversion%5D=master&versionDescriptor%5BversionOptions%5D=0&versionDescriptor%5BversionType%5D=0&resolveLfs=true&%24format=octetStream&api-version=5.0&path=%2F20190424141834.png"/>
            <img style="width:5rem; padding-left: 2rem;" 
            src="https://dev.azure.com/cjlspy/844861b8-ee45-48a3-b430-b974684baaaa/_apis/git/repositories/bf4da1c1-1cfa-4f54-8a75-745cce2b737c/items?versionDescriptor%5Bversion%5D=master&versionDescriptor%5BversionOptions%5D=0&versionDescriptor%5BversionType%5D=0&resolveLfs=true&%24format=octetStream&api-version=5.0&path=%2F20190424141931.png"/>
            <img style="width:5rem; padding-left: 2rem;" 
            src="https://dev.azure.com/cjlspy/844861b8-ee45-48a3-b430-b974684baaaa/_apis/git/repositories/bf4da1c1-1cfa-4f54-8a75-745cce2b737c/items?versionDescriptor%5Bversion%5D=master&versionDescriptor%5BversionOptions%5D=0&versionDescriptor%5BversionType%5D=0&resolveLfs=true&%24format=octetStream&api-version=5.0&path=%2F20190424142013.png"/>
        </div>
        &copy; 2018 <a href="http://www.kikt.top">caijinglong的博客 By caijinglong</a>.
        Powered by <a rel="nofollow noreferer noopener" href="https://gohugo.io" target="_blank">Hugo</a>.
        <a href="http://www.flysnow.org/" target="_blank">Theme</a> based on <a href="https://github.com/rujews/maupassant-hugo" target="_blank">maupassant</a>.<br/>
        本站所有内容基于<a href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh" style="color:coral">CC4.0</a>协议发布,需要转载必须署名
        <br/>
        如果有问题可以<a href="mailto:cjl_spy@163.com">email联系我</a>
        <br/>
        京ICP备18038473号-1
        
    </div>
</footer>


    <script type="text/javascript" src="/js/prism.js" async="true"></script>
    <script type="text/javascript">
    
    (function(){
        $("pre code").parent().addClass("line-numbers")
    }())

    window.MathJax = {
        tex2jax: {
            inlineMath: [ ['$','$'] ],
            processEscapes: true
        }
    };
    </script>
    <script src='https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-MML-AM_CHTML' async></script>

<a id="rocket" href="#top"></a>
<script type="text/javascript" src="/js/totop.js?v=0.0.0" async=""></script>



<script type="text/javascript" src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js" async></script>




</body>
</html>
